大规模计算--分布式tensorflow
1. 团队愿景--实现大规模机器学习
1.1 大规模机器学习特点
- 训练输入量大 (硬盘IO 内存)
- 训练模型大,参数存储量大 (内存,硬盘IO)
- 计算量大(CPU/GPU),涉及高阶向量计算
如何组织分配各节点的计算(CPU/GPU) 内存(MEM) 网络、硬盘的等物理硬件资源成为挑战
说明:
1.个节点可能为异构系统,各节点硬件资源、OS系统配置不一样
1.2 Tensoflow特点
- 前端构建有向无环图 DAG
- 后端c++运行
- 独特的Tensorflow 变量系统的设计,变量系统的更新体系
2. 团队组成--分布式计算架构
物理拓扑结构
- 基础:
各节点有完整的的网络配置、CPU、内存、GPU配置(可选)
(PS架构)
Parameter server架构(PS架构)是深度学习最常采用的分布式训练架构。在PS架构中,集群中的节点被分为两类:parameter server和worker。其中parameter server存放模型的参数,而worker负责计算参数的梯度。在每个迭代过程,worker从parameter sever中获得参数,然后将计算的梯度返回给parameter server,parameter server聚合从worker传回的梯度,然后更新参数,并将新的参数广播给worker。
PS架构中,当worker数量较多时,ps节点的网络带宽将成为系统的瓶颈。
Ring AllReduce架构中各个设备都是worker,没有中心节点来聚合所有worker计算的梯度。Ring AllReduce算法将 device 放置在一个逻辑环路(logical ring)中。每个 device 从上行的device 接收数据,并向下行的 deivce 发送数据,因此可以充分利用每个 device 的上下行带宽。
Ring AllReduce架构
2017年2月百度在PaddlePaddle平台上首次引入了ring-allreduce的架构,随后将其提交到tensorflow的contrib package中。
同年8月,Uber为tensorflow平台开源了一个更加易用和高效的ring allreduce分布式训练库Horovod。
最后,tensorflow官方终于也在1.11版本中支持了allreduce的分布式训练策略CollectiveAllReduceStrategy,其跟estimator配合使用非常方便,只需要构造tf.estimator.RunConfig对象时传入CollectiveAllReduceStrategy参数即可。
3. 团队沟通--通信
grpc
内部通讯
4. 团队运作--job
集群可以完成一个或多个工作job,每一个Job由一些任务task组成
job
对于基于tensorflow框架的深度学习工作job而言,job一般可以分为:
1. 参数作业 (parameter job): 负责管理参数的存储和更新。主要为变量 求和平均、更新
2. 工作节点作业 (worker job):负责管理无状态且主要从事计算的任务,如运行操作op(operation)
task
每个Task对应一个TensorFlow Server服务,对应一个单独的进程。一个Task属于某个Job,通过一个index来标记它在对应Job的tasks中的位置。
每个TensorFlow应用(包括单机模式下)均实现了Master service和Worker service。Master service用来与集群内的worker services进行grpc交互。Worker service则是用本节点上的硬件 local device来计算subgraph(子图,未必是前端构建的完整的DAG,见后面详细解释)。
5. 团队公共资源分配
5.1 模型复制(replicating)
前端构建的DAG怎么分配到各节点上--模型复制(replicating)
DAG 可以直接复制(或部分复制)到各节点上,各节点得到模型副本(model replica),根据复制类型的不同,分为:
1. 数据并行
将图完整的复制到各个节点,将输入数据切割成不同部分,按调度计划feed给各个节点 完成DAG计算
-
模型并行
将图切割(剪枝)成各子图,数据不切割,按调度计划 依次通过各子图进行计算 -
数据模型并行
完成图复制到每个节点上,模型副本再切割分给不同CPU/GPU设备,数据也切割给各个节点
5.2 基于 Tensorflow 特点的 DAG分配
机器学习的特点
1. 变量更新-->适合CPU
2. 矩阵运算--> 合适GPU
1. 从模型并行的角度
让合适的设备(节点)干合适的事情,将完整的运算DAG图分成:
parameter job
worker job
parameter job 部署在 parameter Device(参数设备,可以是节点,也可以是某个CPU/GPU 计算设备)
worker job 部署在 Work Device(工作设备,也可以是某个CPU/GPU 计算设备)
基于设备性能瓶颈,parameter job、worker job可以再划分子图
2. 从数据并行的角度
哪有图的完整复制replica,那就有数据并行
数据并行带来的问题在于最终的模型是一个模型,再确定DAG模型的基础上,参数更新机制就成为关键。
1. 同步更新
数据并行后,等待各个模型副本(model replica)计算结果回归到参数设备上,通过一定规则(如求平均值)统一后,在分发给各个模型副本 。
同步训练看起来很不错,但是实际上需要各个设备的计算能力要均衡,而且要求集群的通信也要均衡,类似于木桶效应,一个拖油瓶会严重拖慢训练进度,所以同步训练方式相对来说训练速度会慢一些。
2. 异步更新
各个模型副本(model replica)单独计算,无需等待。整个模型参数取时间上最新的。在每一轮迭代时,不同设备会读取参数最新的取值,但因为不同设备读取参数取值的时间不一样,所以得到的值也有可能不一样。
t0 时刻--刚开始,大家都一样参数为P0,任务给 D1 D2 D3 完成
t1 时刻--D1 先算完 P1 参数P->P1
t2 时刻--D2 后算完P2,并不知道P0更新了,参数P1->P2
t3 时刻--D3 开始算了,直接P2 输入,算出P3
但是异步情况下,某个设备完成一步训练后,可能发现模型参数已经被其它设备更新过了,此时这个设备计算出的梯度就过期了。
虽然异步模式理论上存在缺陷,但因为训练深度学习模型时使用的随机梯度下降本身就是梯度下降的一个近似解法,而且即使是梯度下降也无法保证达到全局最优值。在实际应用中,在相同时间内使用异步模式训练的模型不一定比同步模式差。所以这两种训练模式在实践中都有非常广泛的应用。
3. 数据模型并行
如果可以 parameter job 放一个设备,worker job 通过模型复制 到多个设备中
6. 容错机制
节点问题
同时机器运作设置检查点checkpoint,作为容错机制。一般都会选择一个chief worker(通常是worker0),用来做训练状态的 checkpoint
tensorflow的分布式功能中有几个点需要强调一下:
图14
Saver的sharded参数设置为True, 也就是设置分片数据。如上图所示,我们这个例子中有3个PS任务,所以分片为3份来并行写入。
如果你使用的是图间复制,你可以选择一个或多个worker任务来回应写入要求。一般来说我们用woker task0来执行一些额为的任务,因为worker是从0开始计数,所以总有worker0存在。这个任务我们称为chief task, 主要负责一些重要的维护任务,比如写入检查点,初始化参数,以及在tensor board中记录一些总结。
Saver现在支持分布式文件系统,比如Google ML, 或者如果你跑在一个Hadoop集群上,你可以写入一个HDFS。
容错性
上面的Saver可以防止运行过程中出错,那么当不幸真的遇到了错误呢?
遇到的错误可以分为以下几种:
最好的情况就是非Chief的worker task出错了,因为这些task实际上是无状态的。那么当遇到了这种错误,当这样的一个worker task恢复的时候,它会重新与它的PS task中建立连接,并且重新开始之前崩溃过的进程。
图15
比较差的一种情况就是PS task失败了,那么就有一个麻烦,因为PS task是有状态的,所有的worker task需要依赖他们来发送他们的梯度并且取得新的参数值。所以这种情况下,他们的chief worker task负责监测这种错误,如果发生了这种错误,chief worker task就打断整个训练,并从上一个检查点恢复所有的PS tasks。
图16
最糟糕的情况就是chief worker task失败了,因为我们让它负责其他所有的任务,我们就要确保它失败了,能够让集群里所有的任务都回到一个很好的状态。所以我们做的就是打断训练,并在当它恢复了时候从上一个好的检查点恢复。这样的处理方式很简单,但也依赖于机器的健壮性。
这里也抛出了一个想法,或许你可以使用一个配置比如Hadoop ZooKeeper 或Etcd来选举chief worker task而不是简单地定义为task0.
图17
Fault tolerance 的API
下面就来介绍一下tensorflow中的容错性一些API。
MonitoredTrainingSession:
首先,我们看一下单线程的代码是怎么写的,一般就是初始化参数,或从一个检查点恢复参数:
而使用了MonitoredTrainingSession的代码会自动帮你初始化参数,并且当PS 任务失败会自动恢复。
单机--多CPU/GPU
同步更新与异步更新有图内模式(in-graph pattern)和图间模式(between-graph pattern)两 种模式,是独立于图内(in-graph)和图间(between-graph)的概念,也就是说无论是图本还是 图间都可以实现同步更新和异步更新,只是实现代码上会有些差异。
图内复制(in-graph replication)是指所有操作(operation)都在同一个图中,用一个客户 端来生成图,然后把所有操作分配到集群的所有参数服务器和工作节点上。图内复制和单机多 卡有点类似,是扩展到了多机多卡,但是数据分发还是在客户端一个节点上。这种方式的优势 是计算节点只需要调用 join()函数等待任务,客户端随时提交数据就可以训练。
但劣势是训练数 据的分发在一个节点上,要分发给不同的工作节点,严重影响并发训练速度。因此,在数据量 很大的情况下,不推荐使用这种模式。
图间复制(between-graph replication)与图本复制对应,是指每一个工作节点创建一个图, 训练的参数保存在参数服务器,数据不用分发,各个工作节点独立计算,计算完成后,把要更 新的参数告诉参数服务器,参数服务器来更新参数。这种模式的优势是不需要数据分发,各个 工作节点都会创建图和读取数据进行训练。劣势是工作节点既是图的创建者又是计算任务的执 行者,如果某个工作节点宕机会影响集群的工作。这种模式是在数据量在 TB 级的时候,并发 性能很高。
因此,大数据相关的深度学习还是推荐使用图间模式。在 14.6 节的对 MNIST 进行 分布式训练的例子中,我们就采用了这种方式。